/**
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package us.levk.remote.drmaa.server;

import java.io.File;
import java.util.AbstractMap;
import java.util.AbstractSet;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.security.auth.login.AppConfigurationEntry;
import javax.security.auth.login.AppConfigurationEntry.LoginModuleControlFlag;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.HierarchicalConfiguration;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.log4j.Logger;


/**
 * @author levk
 *
 */
public abstract class Configuration {

  private static final transient Logger LOG = Logger.getLogger (Configuration.class);

  private static final transient Configuration INSTANCE = new Configuration () {

    private final transient XMLConfiguration CONFIGURATION;

    {
      XMLConfiguration config = null;
      try {
        config = new XMLConfiguration (System.getenv ("REMOTE_DRMAA_SERVER_XML"));
        LOG.info ("Loaded configuration from " + System.getenv ("REMOTE_DRMAA_SERVER_XML"));
      } catch (ConfigurationException e1) {
        try {
          config = new XMLConfiguration (System.getProperty ("user.home") + File.separator + ".remote-drmaa-server.xml");
          LOG.info ("Loaded configuration from " + System.getProperty ("user.home") + File.separator + ".remote-drmaa-server.xml");
        } catch (ConfigurationException e2) {
          try {
            config = new XMLConfiguration (System.getProperty ("java.home") + File.separator + ".remote-drmaa-server.xml");
            LOG.info ("Loaded configuration from " + System.getProperty ("java.home") + File.separator + ".remote-drmaa-server.xml");
          } catch (ConfigurationException e3) {
            config = new XMLConfiguration ();
            LOG.error ("Unable to load configuration");
          }
        }
      }
      CONFIGURATION = config;
    }

    @Override public boolean isAuthenticationEnabled () { return CONFIGURATION.getBoolean ("authentication.enabled"); }

    @Override public String getContact () { return CONFIGURATION.getString ("contact"); }

    @Override public AppConfigurationEntry [] getAuthenticationConfigurationEntries () {
      ArrayList<AppConfigurationEntry> result = new ArrayList<AppConfigurationEntry> ();
      for (final HierarchicalConfiguration c : CONFIGURATION.configurationsAt ("authentication.configuration.modules")) 
        result.add (new AppConfigurationEntry (c.getString ("class"), 
                                               parseFlag (c.getString ("flag")),
                                               new AbstractMap<String, String> () {

                                                 @Override public Set<java.util.Map.Entry<String, String>> entrySet () {
                                                   return new AbstractSet<Map.Entry<String, String>> () {

                                                     final List<HierarchicalConfiguration> options 
                                                       = CONFIGURATION.configurationsAt ("options.option");

                                                     @Override public Iterator<Map.Entry<String, String>> iterator () {
                                                       final Iterator<HierarchicalConfiguration> that = options.iterator ();
                                                       return new Iterator<Map.Entry<String, String>> () {

                                                         @Override public boolean hasNext () { return that.hasNext (); }

                                                         @Override public Map.Entry<String, String> next () {
                                                           final HierarchicalConfiguration n = that.next ();
                                                           return new Map.Entry<String, String>() {

                                                             @Override public String getKey () { return n.getString ("name"); }

                                                             @Override public String getValue () { return n.getString ("value"); }

                                                             @Override public String setValue (String value) { 
                                                               throw new UnsupportedOperationException ();
                                                             }
                                                           };
                                                         }

                                                         @Override public void remove () { throw new UnsupportedOperationException (); }
                                                       };
                                                     }

                                                     @Override public int size () { return options.size (); }
                                                   };
                                                 }
                                               }));
      return result.toArray (new AppConfigurationEntry [0]);
    }

    private LoginModuleControlFlag parseFlag (String flag) {
      if ("required".equalsIgnoreCase (flag)) return LoginModuleControlFlag.REQUIRED;
      else if ("optional".equalsIgnoreCase (flag)) return LoginModuleControlFlag.OPTIONAL;
      else if ("requisite".equalsIgnoreCase (flag)) return LoginModuleControlFlag.REQUISITE;
      else if ("sufficient".equalsIgnoreCase (flag)) return LoginModuleControlFlag.SUFFICIENT;
      else throw new IllegalArgumentException ("Unable to parse flag " + flag);
    }
  };

  public static Configuration getConfiguration () { return INSTANCE; }

  public abstract boolean isAuthenticationEnabled ();

  public abstract String getContact ();

  public abstract AppConfigurationEntry[] getAuthenticationConfigurationEntries ();
}
